#ifndef _UDP_SERVER_
#define _UDP_SERVER_

#include "log.hpp"
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// 服务器封装
class UDPServer
{
public:
    UDPServer(uint16_t port, std::string ip = "")
        : _SRCip(ip), _SRCport(port), _socket(-1)
    {
    }

    // UDP服务器初始化:创建套接字+绑定
    void initserver()
    {
        // 创建套接字
        _socket = socket(AF_INET, SOCK_DGRAM, 0); // 网络-IPV4  面向数据报 协议-填0即可，会根据前面两个选项进行判断
        if (_socket < 0)
        {
            // 返回-1表示创建套接字失败，致命错误
            logMessage(FATAL, "套接字创建失败-%d:%s", errno, strerror(errno));
            exit(1);
        }

        // 绑定本地进程
        struct sockaddr_in local; // 注意头文件必须包含完 man in_addr
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;                                                      // 协议家族
        local.sin_port = htons(_SRCport);                                                // 注意，此处是要发送到网上的，需要转化为网络字节序，使用接口 hton 本地转网络 s是2字节16位
        local.sin_addr.s_addr = _SRCip.empty() ? INADDR_ANY : inet_addr(_SRCip.c_str()); // 如果为空，设置默认ip，此时可以接收任意ip发送的消息，不局限于一个ip。
        // 上述套接字结构初始化完毕，现在进行绑定
        if (bind(_socket, (struct sockaddr *)&local, sizeof local) < 0)
        {
            // 小于零绑定失败！
            logMessage(FATAL, "绑定失败-%d:%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "UDP服务器初始化成功..... %s", strerror(errno));
        // UDP无连接 -初始化完成-
    }

    // UDP服务器通信开始！
    void start()
    {
        // 正式启动UDP服务器
        // 1.0版本 UDP接收客户端信息，返回给客户端本身消息
        char buffer[1024];
        while (true) // 常驻进程，永远不退出
        {
            // 创建客户端套接字结构，用来接收
            struct sockaddr_in client;
            socklen_t clientlen = sizeof(client);
            bzero(&client, sizeof client); // 清零空间
            // 面向数据报接收消息
            ssize_t n = recvfrom(_socket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &clientlen); // 0阻塞读取
            if (n > 0)
            {
                buffer[n] = '\0';
                // 可以把对应主机的套接字信息提取出来
                std::string clientip = inet_ntoa(client.sin_addr); // 网络 转化ip
                uint16_t clientport = ntohs(client.sin_port);
                printf("[%s: %d]# %s\n", clientip.c_str(), clientport, buffer);
            }
            // 发送对应主机
            sendto(_socket, buffer, strlen(buffer), 0, (struct sockaddr *)&client, sizeof(client));
        }
    }

    ~UDPServer()
    {
        if (_socket != -1)
            close(_socket);
    }

private:
    // 源套接字 = 源ip + 源端口
    std::string _SRCip;
    uint16_t _SRCport;
    // UDP对应一套读写套接字对应文件描述符
    int _socket;
};

#endif